package avicit.bdp.dms.oauth2.token;

import avicit.bdp.common.utils.ConfigUtils;
import avicit.platform6.core.application.ApplicationContextHelper;
import avicit.platform6.core.exception.BusinessException;
import avicit.platform6.core.properties.PlatformProperties;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTCreationException;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import org.apache.commons.lang3.StringUtils;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.UUID;

/**
 * TODO
 *
 * @author xugb
 * @date 2024/4/15 9:30
 */

public class JWTOauthUtils {
    private static final String ISSUER = PlatformProperties.getString("platform.token.issuer", "AVIC-DIGITAL");
    private static final String SECRET = "avic-digital";

    public JWTOauthUtils() {
    }

    public static AccessToken createAuthAccessToken(String clientId, String scope, String userId, String appId, String languageCode, String orgId, String deptId, String deptCode, String deptName, String accessKeyId, String secretLevel, String loginName, String userName, boolean needRefresh, String expireTime, String refreshTime) {
        AccessToken data = new AccessToken();
        if(StringUtils.isBlank(expireTime) || "null".equalsIgnoreCase(expireTime)){
            expireTime = ConfigUtils.getInstance().getString("asset.oauth.expireTime", "7200");
        }
        if(StringUtils.isBlank(refreshTime) || "null".equalsIgnoreCase(refreshTime)){
            refreshTime = ConfigUtils.getInstance().getString("asset.oauth.refreshTime", "172800");
        }

        String accessToken = createToken(clientId, scope, userId, appId, languageCode, orgId, deptId, deptCode, deptName, accessKeyId, secretLevel, loginName, userName, "", expireTime);
        String timestamp = getIssuedTimstamp(accessToken);
        String internalToken;
        if (needRefresh) {
            internalToken = createRefreshToken(clientId, scope, userId, appId, languageCode, orgId, deptId, deptCode, deptName, accessKeyId, secretLevel, loginName, userName, timestamp, refreshTime);
            data.setRefreshToken(internalToken);
            data.setRefreshTime(refreshTime);
        }

        internalToken = createInternalToken(clientId, scope, userId, appId, languageCode, orgId, deptId, deptCode, deptName, accessKeyId, secretLevel, loginName, userName, timestamp, expireTime);
        data.setAccessToken(accessToken);
        data.setExpireTime(expireTime);
        data.setInternalToken(internalToken);
        Date createDate = new Date(Long.parseLong(timestamp));
        data.setCreateTime(createDate);
        return data;
    }

    public static String createToken(String clientId, String scope, String userId, String appId, String languageCode, String orgId, String deptId, String deptCode, String deptName, String accessKeyId, String secretLevel, String loginName, String userName, String timestamp, String expireTime) {
        try {
            Map<String, String> props = getPropsMap(appId, languageCode, orgId, deptId, deptCode, deptName, accessKeyId, secretLevel, loginName, userName);
            props.put("TOKEN_TYPE", "auth_access");
            props.put("clientId", clientId);
            if (StringUtils.isNotBlank(scope)) {
                props.put("scope", scope);
            }

            return createToken(userId, props, timestamp, expireTime);
        } catch (JWTCreationException var16) {
            throw new BusinessException(var16.getMessage(), var16);
        }
    }

    public static String createInternalToken(String clientId, String scope, String userId, String appId, String languageCode, String orgId, String deptId, String deptCode, String deptName, String accessKeyId, String secretLevel, String loginName, String userName, String timestamp, String expireTime) {
        try {
            Map<String, String> props = getPropsMap(appId, languageCode, orgId, deptId, deptCode, deptName, accessKeyId, secretLevel, loginName, userName);
            props.put("TOKEN_TYPE", "access");
            props.put("clientId", clientId);
            if (StringUtils.isNotBlank(scope)) {
                props.put("scope", scope);
            }

            return createToken(userId, props, timestamp, expireTime);
        } catch (JWTCreationException var16) {
            throw new BusinessException(var16.getMessage(), var16);
        }
    }

    public static String createRefreshToken(String clientId, String scope, String userId, String appId, String languageCode, String orgId, String deptId, String deptCode, String deptName, String accessKeyId, String secretLevel, String loginName, String userName, String timestamp, String expireTime) {
        try {
            Map<String, String> props = getPropsMap(appId, languageCode, orgId, deptId, deptCode, deptName, accessKeyId, secretLevel, loginName, userName);
            props.put("TOKEN_TYPE", "auth_refresh");
            props.put("clientId", clientId);
            if (StringUtils.isNotBlank(scope)) {
                props.put("scope", scope);
            }

            return createToken(userId, props, timestamp, expireTime);
        } catch (JWTCreationException var16) {
            throw new BusinessException(var16.getMessage(), var16);
        }
    }

    private static Map<String, String> getPropsMap(String appId, String languageCode, String orgId, String deptId, String deptCode, String deptName, String accessKeyId, String secretLevel, String loginName, String userName) {
        Map<String, String> props = new HashMap();
        if (StringUtils.isNotBlank(appId)) {
            props.put("APP_ID", appId);
        } else {
            props.put("APP_ID", ApplicationContextHelper.getApplicationId());
        }

        if (StringUtils.isNotBlank(languageCode)) {
            props.put("LANGUAGE_CODE", languageCode);
        } else {
            props.put("LANGUAGE_CODE", "zh_CN");
        }

        if (StringUtils.isNotBlank(orgId)) {
            props.put("ORG_IDENTITY", orgId);
        }

        if (StringUtils.isNotBlank(deptId)) {
            props.put("USER_DEPT_ID", deptId);
        }

        if (StringUtils.isNotBlank(deptCode)) {
            props.put("USER_DEPT_CODE", deptCode);
        }

        if (StringUtils.isNotBlank(deptName)) {
            props.put("USER_DEPT_NAME", deptName);
        }

        if (StringUtils.isNotBlank(accessKeyId)) {
            props.put("AVICIT_PT6_APP_USER_ACCESS_KEY_ID", accessKeyId);
        }

        if (StringUtils.isNotBlank(secretLevel)) {
            props.put("SECRET_LEVEL", secretLevel);
        }

        if (StringUtils.isNotBlank(loginName)) {
            props.put("USER_CODE", loginName);
        }

        if (StringUtils.isNotBlank(userName)) {
            props.put("USER_NAME", userName);
        }

        return props;
    }

    private static String createToken(String userId, Map<String, String> props, String timestamp, String expireTime) {
        try {
            Algorithm algorithm = Algorithm.HMAC256("avic-digital");
            Date fakeNow;
            Date expire;
            if (StringUtils.isNotBlank(timestamp)) {
                fakeNow = new Date(Long.parseLong(timestamp));
                expire = new Date(Long.parseLong(timestamp) + Long.parseLong(expireTime) * 1000L);
            } else {
                LocalDateTime nowDateTime = LocalDateTime.now();
                LocalDateTime fakeNowDateTime = nowDateTime.minusSeconds(10L);
                LocalDateTime expireDateTime = fakeNowDateTime.plusSeconds(Long.parseLong(expireTime));
                fakeNow = Date.from(fakeNowDateTime.atZone(ZoneId.systemDefault()).toInstant());
                expire = Date.from(expireDateTime.atZone(ZoneId.systemDefault()).toInstant());
            }

            JWTCreator.Builder jwtBuilder = JWT.create().withIssuer(ISSUER).withSubject("App前端授权").withAudience(new String[]{userId}).withJWTId(UUID.randomUUID().toString());
            if (timestamp != null) {
                jwtBuilder.withClaim("ISSUED_TIMESTAMP", String.valueOf(fakeNow.getTime()));
                jwtBuilder.withExpiresAt(expire).withIssuedAt(fakeNow);
            }

            if (props != null && !props.isEmpty()) {
                Iterator var12 = props.entrySet().iterator();

                while(var12.hasNext()) {
                    Map.Entry<String, String> entry = (Map.Entry)var12.next();
                    jwtBuilder.withClaim((String)entry.getKey(), (String)entry.getValue());
                }
            }

            return jwtBuilder.sign(algorithm);
        } catch (JWTCreationException var10) {
            throw new BusinessException(var10.getMessage(), var10);
        }
    }

    public static void verifyToken(String token) {
        if (StringUtils.isBlank(token)) {
            throw new BusinessException("Token lost.");
        } else {
            try {
                Algorithm algorithm = Algorithm.HMAC256("avic-digital");
                JWTVerifier verifier = JWT.require(algorithm).withIssuer(new String[]{ISSUER}).withClaim("TOKEN_TYPE", "auth_access").build();
                verifier.verify(token);
            } catch (JWTVerificationException var4) {
                throw var4;
            }
        }
    }

    public static void verifyRefreshToken(String token) {
        if (StringUtils.isBlank(token)) {
            throw new BusinessException("Token lost.");
        } else {
            try {
                Algorithm algorithm = Algorithm.HMAC256("avic-digital");
                JWTVerifier verifier = JWT.require(algorithm).withIssuer(new String[]{ISSUER}).withClaim("TOKEN_TYPE", "auth_refresh").build();
                verifier.verify(token);
            } catch (JWTVerificationException var4) {
                throw var4;
            }
        }
    }

    public static DecodedJWT decodeToken(String token) {
        try {
            DecodedJWT jwt = JWT.decode(token);
            String alg = jwt.getAlgorithm();
            String typ = jwt.getType();
            String iss = jwt.getIssuer();
            Date iat = jwt.getIssuedAt();
            String sub = jwt.getSubject();
            List<String> aud = jwt.getAudience();
            Date nbf = jwt.getNotBefore();
            Date exp = jwt.getExpiresAt();
            String jti = jwt.getId();
            return jwt;
        } catch (JWTDecodeException var11) {
            throw new BusinessException(var11.getMessage(), var11);
        }
    }

    public static String getIssuedTimstamp(String token) {
        DecodedJWT jwt = JWT.decode(token);
        Claim claim = jwt.getClaim("ISSUED_TIMESTAMP");
        return claim == null ? "" : claim.asString();
    }
}
